package com.cqsd.string;

import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;

/**
 * @author caseycheng
 * @date 2023/03/12-22:42
 */
final public class ParseExpr {
    public enum Kind {
        /**
         * 开始节点%
         */
        start,
        /**
         * 左开节点{
         */
        left_sep,
        /**
         * 右开节点}
         */
        right_sep,
        /**
         * 文本节点
         */
        text;

        @Contract(pure = true)
        public static Kind expr(Character character) {
            //这东西，语法检测没有上去
            //noinspection AlibabaSwitchStatement
            switch (character) {
                case '$' -> {
                    return Kind.start;
                }
                case '{' -> {
                    return Kind.left_sep;
                }
                case '}' -> {
                    return Kind.right_sep;
                }
                default -> {
                    return Kind.text;
                }
            }
        }
    }

    /**
     * 系统指针
     */
    private int size = 0;
    /**
     * 最大寻址长度
     */
    private final int max;
    /**
     * 需要解析的内容
     */
    private final char[] content;
    /**
     * 用来标识当前对象是否为表达式,默认认为是表达式
     */
    private boolean exprFlag = true;
    /**
     * 是否解析完成
     */
    private boolean end = false;
    /**
     * 最终结果
     */
    private String result = null;

    /**
     * 使用状态机来解析内容
     * 锤子个状态机，按照解析规则就行
     *
     * @param expr
     */
    public ParseExpr(@NotNull String expr) {
        this.max = expr.length();
        this.content = expr.toCharArray();
    }


    /**
     * 开始解析
     */
    public void parse() {
        //吃掉空格
        eatSpace();
        //吃掉开始的$ 会在这个方法里判断是否为一个表达式 如果不是$开头就会标识为已结束
        eatStartKind();
        //判断是否可解析
        if (exprFlag) {
            //创建字符缓冲池
            final var sb = new StringBuilder();
            //吃掉{
            eatLeftSepKind();
            //解析文本，直到遇到}
            parseText(sb);
            //吃掉}
            eatRightSep();
            this.result = sb.toString();
        } else {
            //直接给结果赋值
            this.result = String.valueOf(this.content);
        }
    }

    /**
     * 取得结果
     *
     * @return
     */
    public String result() {
        if (hasEnd()) {
            return this.result;
        } else {
            throw new RuntimeException("尚未解析内容");
        }
    }

    /**
     * 标识是否为表达式 是表达式就返回true
     *
     * @return
     */
    public boolean hasExpr() {
        return this.exprFlag;
    }

    /**
     * 返回是否进行了解析
     *
     * @return
     */
    public boolean hasEnd() {
        return end;
    }

    /**
     * 结尾 判断是否是}结尾，如果不是就代表表达式不规范，重写
     */
    private void eatRightSep() {
        eatSpace();
        final var c = current();
        final var kind = Kind.expr(c);
        if (kind != Kind.right_sep) {
            throw new RuntimeException("不是一个表达式语句 预期为}");
        } else {
            //            next();
            //这里已经解析完成，不用下移指针了
            end = true;
        }
    }

    /**
     * 开始对文本进行解析
     *
     * @param sb 需要将解析到的文本添加到缓冲池中
     */
    private void parseText(StringBuilder sb) {
        //还是默认调用一下吃掉空格
        eatSpace();
        //对当前语句进行循环判断 判断是否能被解析为 text，不是text就代表解析完毕。
        for (var item = next(); Kind.expr(item) == Kind.text; item = next()) {
            sb.append(item);
        }
    }

    /**
     * 吃掉左边{
     */
    private void eatLeftSepKind() {
        //默认调用一下去掉空格
        eatSpace();
        final var c = current();
        final var kind = Kind.expr(c);
        if (kind != Kind.left_sep) {
            throw new RuntimeException("不是一个表达式语句 预期为{");
        } else {
            next();
        }
    }

    /**
     * 去掉空格
     */
    private void eatSpace() {
        //检测当前字符是不是空格，如果是空格那么就指针下移动再继续检测，这就是一个空循环。
        for (var i = current(); Character.isSpaceChar(i); i = next()) {

        }
    }

    /**
     * 吃掉开始节点
     */
    private void eatStartKind() {
        //获取当前字符
        final var c = current();
        //转化为枚举
        final var kind = Kind.expr(c);
        //如果不是$开头就标识当前语句不是一个表达式，后面那些针对表达式的方法可以不用运行。
        if (kind != Kind.start) {
            this.exprFlag = false;
            this.end = true;
        } else {
            //在这里的话就是一个表达式开头，那么指针下移吃掉$
            next();
        }
    }

    private boolean hasNext() {
        return this.size < this.max;
    }

    private char current() {
        if (hasNext()) {
            return this.content[this.size];
        } else {
            return this.content[this.size - 1];
        }
    }

    private char next() {
        if (hasNext()) {
            return this.content[this.size++];
        }
        return this.content[this.size];
    }

}
